home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995 August: Tool Chest / Dev.CD Aug 95 TC / Dev.CD Aug 95 TC.toast / New System Software Extensions / QuickDraw™ GX 1.1.2 / Programming Stuff / QuickDraw™ GX Libraries / Graphics libraries / selection library.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-10  |  33.7 KB  |  1,096 lines  |  [TEXT/MPS ]

  1. /* selection library.c -- SelectionHandle manipulation routines.
  2.         
  3. CHANGE LOG
  4.  
  5. Date            Person        Action
  6. ----            ------        ------
  7.  
  8. 911031        DGO            • Created module.
  9. 920130        DGO            • Canonicalized order for range selections.
  10. 940606        DGO            •    Minor bugfixes.
  11.  
  12. */
  13.  
  14. /* Copyright ©1991-1994 Apple Computer, Inc.    All rights reserved. */
  15.  
  16. #include <Types.h>
  17. #include <Memory.h>
  18.     #include <OSUtils.h>
  19.  
  20. #ifndef selectionLibraryIncludes
  21. #include "selection library.h"
  22. #endif
  23.  
  24. #ifndef layoutTypesIncludes
  25. #include "layout types.h"
  26. #endif
  27.  
  28. #ifndef layoutRoutinesIncludes
  29. #include "layout routines.h"
  30. #endif
  31.  
  32. #ifndef graphicsRoutinesIncludes
  33. #include "graphics routines.h"
  34. #endif
  35.  
  36. #define siSize sizeof(Selection)
  37. #define sioSize (siSize + sizeof(SelectionOffsetRange))
  38.  
  39. #define MyMin(a,b) (((a) < (b)) ? (a) : (b))
  40. #define MyMax(a,b) (((a) > (b)) ? (a) : (b))
  41.  
  42. typedef enum {
  43.     unionAll,
  44.     unionOne,
  45.     firstIsLow,
  46.     secondIsLow
  47.     } UnionOneState;
  48.  
  49. /* ---------------------------------------------------------------------------------- */
  50.  
  51. /* NewEmptySelection creates a Selection of type emptySelection. */
  52.  
  53. SelectionHandle NewEmptySelection()
  54.     {
  55.     Handle                h;
  56.     SelectionPtr    sip;
  57.     if ((h = NewHandle(siSize)) == nil) {GXPostGraphicsError(out_of_memory); return nil;}
  58.     sip = (SelectionPtr) *h;
  59.     sip->type = emptySelection;
  60.     return ((SelectionHandle) h);
  61.     } /* NewEmptySelection */
  62.  
  63. /* ---------------------------------------------------------------------------------- */
  64.  
  65. /* NewCaretSelection creates a Selection of type simpleCaret and fills it with the
  66.         specified offset and leadingEdge information. */
  67.  
  68. SelectionHandle NewCaretSelection(SelectionOffset offset, long leadingEdge)
  69.     {
  70.     Handle                h;
  71.     SelectionPtr    sip;
  72.     if ((h = NewHandle(siSize)) == nil) {GXPostGraphicsError(out_of_memory); return nil;}
  73.     sip = (SelectionPtr) *h;
  74.     sip->type = simpleCaret;
  75.     sip->data.caret.offset = offset;
  76.     sip->data.caret.leadingEdge = (short) leadingEdge;
  77.     return ((SelectionHandle) h);
  78.     } /* NewCaretSelection */
  79.  
  80. /* ---------------------------------------------------------------------------------- */
  81.  
  82. /* NewRangeSelection creates a Selection of type simpleRange and sets it to a single
  83.         range, using the specified data. */
  84.  
  85. SelectionHandle NewRangeSelection(SelectionOffsetRange *range)
  86.     {
  87.     Handle                                h;
  88.     SelectionOffsetRange    sortedRange;
  89.     SelectionPtr                    sip;
  90.     if (range == nil) {GXPostGraphicsError(parameter_is_nil); return nil;}
  91.     if (range->minOffset == range->maxOffset)
  92.         return NewCaretSelection(range->minOffset, false);
  93.     else if (range->minOffset != selectionExtremeEdge && range->maxOffset != selectionExtremeEdge && range->minOffset > range->maxOffset)
  94.         {
  95.         sortedRange.minOffset = range->maxOffset;
  96.         sortedRange.maxOffset = range->minOffset;
  97.         range = &sortedRange;
  98.         }
  99.     if ((h = NewHandle(sioSize)) == nil) {GXPostGraphicsError(out_of_memory); return nil;}
  100.     sip = (SelectionPtr) *h;
  101.     sip->type = simpleRange;
  102.     sip->data.range.rangeCount = 1;
  103.     sip->data.range.ranges[0] = *range;
  104.     return ((SelectionHandle) h);
  105.     } /* NewRangeSelection */
  106.  
  107. /* ---------------------------------------------------------------------------------- */
  108.  
  109. /* NewFullSelection creates a Selection of type simpleRange and sets it to a single
  110.         range, with both ends set to the selectionExtremeEdge value. */
  111.  
  112. SelectionHandle NewFullSelection()
  113.     {
  114.     SelectionOffsetRange    myRange;
  115.     myRange.minOffset = myRange.maxOffset = selectionExtremeEdge;
  116.     return NewRangeSelection(&myRange);
  117.     } /* NewFullSelection */
  118.  
  119. /* ---------------------------------------------------------------------------------- */
  120.  
  121. /* NewStartSelection creates a Selection of type simpleRange and sets it to a single
  122.         range, with the start set to selectionExtremeEdge and the end set to the specified
  123.         offset. */
  124.  
  125. SelectionHandle NewStartSelection(SelectionOffset toOffset)
  126.     {
  127.     SelectionOffsetRange    myRange;
  128.     myRange.minOffset = selectionExtremeEdge;
  129.     myRange.maxOffset = toOffset;
  130.     return NewRangeSelection(&myRange);
  131.     } /* NewStartSelection */
  132.  
  133. /* ---------------------------------------------------------------------------------- */
  134.  
  135. /* NewEndSelection creates a Selection of type simpleRange and sets it to a single
  136.         range, with the start set to the specified offset and the end set to the
  137.         selectionExtremeEdge value. */
  138.  
  139. SelectionHandle NewEndSelection(SelectionOffset fromOffset)
  140.     {
  141.     SelectionOffsetRange    myRange;
  142.     myRange.minOffset = fromOffset;
  143.     myRange.maxOffset = selectionExtremeEdge;
  144.     return NewRangeSelection(&myRange);
  145.     } /* NewEndSelection */
  146.  
  147. /* ---------------------------------------------------------------------------------- */
  148.  
  149. /* SetEmptySelection takes an existing Selection and removes its contents, turning it
  150.         into a Selection of type emptySelection. */
  151.  
  152. void SetEmptySelection(SelectionHandle selection)
  153.     {
  154.     SelectionPtr    sip;
  155.     if (selection == nil || (sip = *selection) == nil)
  156.         {GXPostGraphicsError(parameter_is_nil); return;}
  157.     sip->type = emptySelection;
  158.     } /* SetEmptySelection */
  159.  
  160. /* ---------------------------------------------------------------------------------- */
  161.  
  162. /* SetCaretSelection takes an existing Selection and removes its contents, turning it
  163.         into a Selection of type simpleCaret and copying the specified data into it. */
  164.  
  165. void SetCaretSelection(SelectionHandle selection, SelectionOffset offset, long leadingEdge)
  166.     {
  167.     SelectionPtr    sip;
  168.     if (selection == nil || (sip = *selection) == nil)
  169.         {GXPostGraphicsError(parameter_is_nil); return;}
  170.     sip->type = simpleCaret;
  171.     sip->data.caret.offset = offset;
  172.     sip->data.caret.leadingEdge = (short) leadingEdge;
  173.     } /* SetCaretSelection */
  174.  
  175. /* ---------------------------------------------------------------------------------- */
  176.  
  177. /* SetRangeSelection takes an existing Selection and removes its contents, turning it
  178.         into a Selection of type simpleRange containing a single range with the specified
  179.         offsets. */
  180.  
  181. void SetRangeSelection(SelectionHandle selection, SelectionOffsetRange *range)
  182.     {
  183.     SelectionOffsetRange    sortedRange;
  184.     SelectionPtr                    sip;
  185.     if (selection == nil || (sip = *selection) == nil)
  186.         {GXPostGraphicsError(parameter_is_nil); return;}
  187.     if (range->minOffset == range->maxOffset)
  188.         {
  189.         SetCaretSelection(selection, range->minOffset, false);
  190.         return;
  191.         }
  192.     if (GetHandleSize((Handle) selection) < sioSize)
  193.         { /* need to grow handle */
  194.         SetHandleSize((Handle) selection, sioSize);
  195.         if (MemError())
  196.             {GXPostGraphicsError(out_of_memory); return;}
  197.         sip = *selection; /* might have moved... */
  198.         } /* end need to grow handle */
  199.     if (range->minOffset > range->maxOffset)
  200.         {
  201.         sortedRange.minOffset = range->maxOffset;
  202.         sortedRange.maxOffset = range->minOffset;
  203.         range = &sortedRange;
  204.         }
  205.     sip->type = simpleRange;
  206.     sip->data.range.rangeCount = 1;
  207.     sip->data.range.ranges[0] = *range;
  208.     } /* SetRangeSelection */
  209.  
  210. /* ---------------------------------------------------------------------------------- */
  211.  
  212. /* SetFullSelection takes an existing Selection and removes its contents, turning it
  213.         into a Selection of type simpleRange containing a single range with both offsets
  214.         set to the selectionExtremeEdge value. */
  215.  
  216. void SetFullSelection(SelectionHandle selection)
  217.     {
  218.     SelectionOffsetRange    myRange;
  219.     myRange.minOffset = myRange.maxOffset = selectionExtremeEdge;
  220.     SetRangeSelection(selection, &myRange);
  221.     } /* SetFullSelection */
  222.  
  223. /* ---------------------------------------------------------------------------------- */
  224.  
  225. /* SetStartSelection takes an existing Selection and removes its contents, turning it into
  226.         a Selection of type simpleRange containing a single range with the start offset set
  227.         to the selectionExtremeEdge value and the end offset set to the specified value. */
  228.  
  229. void SetStartSelection(SelectionHandle selection, SelectionOffset toOffset)
  230.     {
  231.     SelectionOffsetRange    myRange;
  232.     myRange.minOffset = selectionExtremeEdge;
  233.     myRange.maxOffset = toOffset;
  234.     SetRangeSelection(selection, &myRange);
  235.     } /* SetStartSelection */
  236.  
  237. /* ---------------------------------------------------------------------------------- */
  238.  
  239. /* SetEndSelection takes an existing Selection and removes its contents, turning it into
  240.         a Selection of type simpleRange containing a single range with the start offset set
  241.         to the specified value and the end offset set to the selectionExtremeEdge value. */
  242.  
  243. void SetEndSelection(SelectionHandle selection, SelectionOffset fromOffset)
  244.     {
  245.     SelectionOffsetRange    myRange;
  246.     myRange.minOffset = fromOffset;
  247.     myRange.maxOffset = selectionExtremeEdge;
  248.     SetRangeSelection(selection, &myRange);
  249.     } /* SetEndSelection */
  250.  
  251. /* ---------------------------------------------------------------------------------- */
  252.  
  253. /* DisposeSelection disposes of the storage associated with a Selection. */
  254.  
  255. void DisposeSelection(SelectionHandle selection)
  256.     {
  257.     if (selection == nil) {GXPostGraphicsError(parameter_is_nil); return;}
  258.     else DisposeHandle((Handle) selection);
  259.     } /* DisposeSelection */
  260.  
  261. /* ---------------------------------------------------------------------------------- */
  262.  
  263. /* GetSelectionType allows procedural access to the type of a Selection. */
  264.  
  265. SelectionType GetSelectionType(SelectionHandle selection)
  266.     {
  267.     SelectionPtr    sip;
  268.     if (selection == nil || (sip = *selection) == nil)
  269.         {GXPostGraphicsError(parameter_is_nil); return emptySelection;}
  270.     else return sip->type;
  271.     } /* GetSelectionType */
  272.  
  273. /* ---------------------------------------------------------------------------------- */
  274.  
  275. /* GetCaretSelection allows procedural access to the character offset and leadingEdge
  276.         value associated with a Selection of type simpleCaret. */
  277.  
  278. SelectionOffset GetCaretSelection(SelectionHandle selection, boolean *leadingEdge)
  279.     {
  280.     SelectionPtr    sip;
  281.     if (selection == nil || (sip = *selection) == nil)
  282.         {GXPostGraphicsError(parameter_is_nil); return 0;}
  283.     if (sip->type != simpleCaret) 
  284.         {
  285. #ifdef debugging
  286.         GXPostGraphicsError(parameter_out_of_range);
  287. #endif
  288.         return 0;
  289.         }
  290.     /* It is not an error for leadingEdge to be nil; in this case, we assume the client
  291.             wasn't interested in the state of the leadingEdge flag. */
  292.     if (leadingEdge) *leadingEdge = (boolean) sip->data.caret.leadingEdge;
  293.     return sip->data.caret.offset;
  294.     } /* GetCaretSelection */
  295.  
  296. /* ---------------------------------------------------------------------------------- */
  297.  
  298. /* GetRangeSelection allows procedural access to the SelectionOffsetRange value(s)
  299.         associated with a Selection of type simpleRange or discontiguousRange. It returns
  300.         the total byte size needed for the resulting SelectionRanges structure; it returns
  301.         this size even if the given selectionRanges argument is nil. */
  302.  
  303. long GetRangeSelection(SelectionHandle selection, SelectionRanges *selectionRanges)
  304.     {
  305.     long                                    totalSize;
  306.     SelectionPtr                    sip;
  307.     SelectionOffsetRange    *pDst, *pSrc;
  308.     short                                 i;
  309.     if (selection == nil || (sip = *selection) == nil)
  310.         {GXPostGraphicsError(parameter_is_nil); return 0;}
  311.     if (sip->type != simpleRange && sip->type != discontiguousRange)
  312.         {
  313. #ifdef debugging
  314.         GXPostGraphicsError(parameter_out_of_range); 
  315. #endif
  316.         return 0;
  317.         }
  318.     totalSize = sizeof(SelectionRanges) + (sip->data.range.rangeCount * sizeof(SelectionOffsetRange));
  319.     if (selectionRanges)
  320.         { /* copy the data */
  321.         selectionRanges->rangeCount = sip->data.range.rangeCount;
  322.         i = (short) selectionRanges->rangeCount;
  323.         pDst = &selectionRanges->ranges[0];
  324.         pSrc = &sip->data.range.ranges[0];
  325.         while (i--) *pDst++ = *pSrc++;
  326.         } /* end copy the data */
  327.     return totalSize;
  328.     } /* GetRangeSelection */
  329.  
  330. /* ---------------------------------------------------------------------------------- */
  331.  
  332. /* XXX Still don't have "equalToSelection" logic in here... XXX */
  333.  
  334. /* RangeInSelection returns an indication of the degree to which the specified offset
  335.         range is contained in the specified Selection. */
  336.  
  337. SelectionMatch RangeInSelection(SelectionHandle selection, SelectionOffsetRange *range)
  338.     {
  339.     SelectionHandle     thisRange;
  340.     SelectionMatch        retVal;
  341.     SelectionPtr            sip;
  342.     
  343.     thisRange = NewRangeSelection(range);
  344.     SectSelection(thisRange, selection);
  345.     sip = *thisRange;
  346.     switch (sip->type)
  347.         {
  348.         case emptySelection:
  349.         case simpleCaret: /* "simpleCaret" hadn't better happen here... */
  350.             retVal = notInSelection;
  351.             break;
  352.         case simpleRange:
  353.             if (range->minOffset == sip->data.range.ranges[0].minOffset && range->maxOffset == sip->data.range.ranges[0].maxOffset)
  354.                 retVal = fullyInSelection;
  355.             else retVal = partlyInSelection;
  356.             break;
  357.         case discontiguousRange:
  358.             retVal = partlyInSelection; /* is this correct? */
  359.             break;
  360.         }
  361.     
  362.     DisposeSelection(thisRange);
  363.     return retVal;
  364.     } /* RangeInSelection */
  365.  
  366. /* ---------------------------------------------------------------------------------- */
  367.  
  368. /* IsFullSelection determines whether a Selection has the special selectionExtremeEdge
  369.         value as both its minOffset and maxOffset values. */
  370.  
  371. static boolean IsFullSelection(SelectionHandle s)
  372.     {
  373.     SelectionPtr                    sip;
  374.     SelectionOffsetRange    *sor;
  375.     
  376.     if (s == nil || (sip = *s) == nil)
  377.         {GXPostGraphicsError(parameter_is_nil); return false;}
  378.     if (sip->type != simpleRange && sip->type != discontiguousRange)
  379.         return false;
  380.     sor = &sip->data.range.ranges[0];
  381.     
  382.     if (sor->minOffset == selectionExtremeEdge && sor->maxOffset == selectionExtremeEdge)
  383.         return true;
  384.     else
  385.         return false;
  386.     
  387.     } /* IsFullSelection */
  388.  
  389. /* ---------------------------------------------------------------------------------- */
  390.  
  391. void InvertSelection(SelectionHandle dest)
  392.     {
  393.     Handle                                tempHandle;
  394.     SelectionPtr                    destSIP;
  395.     SelectionOffsetRange    *newSOR, *nextSOR, *sor;
  396.     short                                 count, newCount;
  397.     
  398.     if (dest == nil || (destSIP = *dest) == nil)
  399.         {GXPostGraphicsError(parameter_is_nil); return;}
  400.     if (destSIP->type == simpleCaret)
  401.         {
  402. #ifdef debugging
  403.         GXPostGraphicsError(parameter_out_of_range);
  404. #endif
  405.         return;
  406.         }
  407.     
  408.     if (IsFullSelection(dest)) SetEmptySelection(dest);
  409.     else if (destSIP->type == emptySelection) SetFullSelection(dest);
  410.     else
  411.         { /* neither empty nor full */
  412.         tempHandle = NewHandle((destSIP->data.range.rangeCount + 1) * sizeof(SelectionOffsetRange));
  413.         if (tempHandle == nil)
  414.             {GXPostGraphicsError(out_of_memory); return;}
  415.         destSIP = *dest;    /* memory might have moved */
  416.         sor = &destSIP->data.range.ranges[0];
  417.         nextSOR = sor + 1;
  418.         count = (short) destSIP->data.range.rangeCount;
  419.         newSOR = (SelectionOffsetRange *) *tempHandle;
  420.         newCount = 0;
  421.         
  422.         /* At some gxPoint, we may want to deal with the problem that a non open-ended
  423.                 selection that starts at zero doesn't get restored after two inversions. */
  424.         
  425.         /* If first is not open-ended, add [selectionExtremeEdge, first.minOffset] */
  426.         if (sor->minOffset != selectionExtremeEdge)
  427.             {
  428.             newSOR->minOffset = selectionExtremeEdge;
  429.             (newSOR++)->maxOffset = sor->minOffset;
  430.             newCount++;
  431.             }
  432.         /* Start main loop */
  433.         while (--count) /* "--count" insures n-1 iterations */
  434.             { /* main loop */
  435.             newSOR->minOffset = sor->maxOffset;
  436.             (newSOR++)->maxOffset = nextSOR->minOffset;
  437.             newCount++;
  438.             sor = nextSOR++;
  439.             } /* end main loop */
  440.         /* If last is not open-ended, add [last.maxOffset, selectionExtremeEdge] */
  441.         if (sor->maxOffset != selectionExtremeEdge)
  442.             {
  443.             newSOR->minOffset = sor->maxOffset;
  444.             newSOR->maxOffset = selectionExtremeEdge;
  445.             newCount++;
  446.             }
  447.         /* Now change the size of dest (if needed) and store the new SORs into it. */
  448.         if (newCount > (short) destSIP->data.range.rangeCount)
  449.             {
  450.             SetHandleSize((Handle) dest, siSize + newCount * sizeof(SelectionOffsetRange));
  451.             destSIP = *dest;    /* memory might have moved */
  452.             }
  453.         destSIP->data.range.rangeCount = newCount;
  454.         destSIP->type = (SelectionType) ((newCount == 1) ? simpleRange : discontiguousRange);
  455.         sor = &destSIP->data.range.ranges[0];
  456.         newSOR = (SelectionOffsetRange *) *tempHandle;
  457.         while (newCount--) *sor++ = *newSOR++;
  458.         DisposeHandle(tempHandle);
  459.         } /* end neither empty nor full */
  460.     
  461.     } /* InvertSelection */
  462.  
  463. /* ---------------------------------------------------------------------------------- */
  464.  
  465. void ShiftSelection(SelectionHandle dest, SelectionOffset delta)
  466.     {
  467.     SelectionOffsetRange    *sor;
  468.     SelectionPtr                    destSIP;
  469.     short                                 count;
  470.     
  471.     if (dest == nil || (destSIP = *dest) == nil)
  472.         {GXPostGraphicsError(parameter_is_nil); return;}
  473.     
  474.     switch (destSIP->type)
  475.         {
  476.         case emptySelection:
  477.             break;
  478.         case simpleCaret:
  479.             destSIP->data.caret.offset += delta;
  480.             break;
  481.         case simpleRange:
  482.         case discontiguousRange:
  483.             count = (short) destSIP->data.range.rangeCount;
  484.             sor = &destSIP->data.range.ranges[0];
  485.             while (count--)
  486.                 {
  487.                 if (sor->minOffset != selectionExtremeEdge) sor->minOffset += delta;
  488.                 if (sor->maxOffset != selectionExtremeEdge) sor->maxOffset += delta;
  489.                 sor++;
  490.                 }
  491.             break;
  492.         } /* end switch */
  493.     
  494.     } /* ShiftSelection */
  495.  
  496. /* ---------------------------------------------------------------------------------- */
  497.  
  498. /* GrowSI is an internal work function used to grow a Selection to allow more
  499.         space for SelectionOffsetRanges. */
  500.  
  501. static void GrowSI(
  502.     Handle                                h,
  503.     short                                 *maxAllocated,
  504.     SelectionPtr                    *sip,
  505.     SelectionOffsetRange    **dest,
  506.     SelectionOffsetRange    **prevDest)
  507.  
  508.     {
  509.     short oldMax = *maxAllocated;
  510.     *maxAllocated <<= 1;
  511.     SetHandleSize(h, siSize + (*maxAllocated) * sizeof(SelectionOffsetRange));
  512.     if (MemError() != noErr) GXPostGraphicsError(out_of_memory);
  513.     *sip = (SelectionPtr) *h;
  514.     *dest = (*sip)->data.range.ranges + oldMax;
  515.     *prevDest = *dest - 1;
  516.     } /* GrowSI */
  517.  
  518. /* ---------------------------------------------------------------------------------- */
  519.  
  520. /* UnionOne takes two SelectionOffsetRanges and determines if their union is representable
  521.         in a single resulting SelectionOffsetRange. If it is not, the dest value is not set,
  522.         and a value is returned that indicates whether the first or second argument starts
  523.         earlier. If it is, the dest value is set to the union, and the unionOne value is
  524.         returned. */
  525.  
  526. static UnionOneState UnionOne(
  527.     SelectionOffsetRange    *sor1,
  528.     SelectionOffsetRange    *sor2,
  529.     SelectionOffsetRange    *dest)
  530.  
  531.     {
  532.     short                 selector;
  533.     UnionOneState retVal;
  534.     
  535.     selector = (short) ((sor1->minOffset == selectionExtremeEdge) << 3);
  536.     selector += (short) ((sor1->maxOffset == selectionExtremeEdge) << 2);
  537.     selector += (short) ((sor2->minOffset == selectionExtremeEdge) << 1);
  538.     selector += (sor2->maxOffset == selectionExtremeEdge);
  539.     
  540.     switch (selector)
  541.         {
  542.         
  543.         /* Case 0 is where neither SelectionOffsetRange is open-ended */
  544.         
  545.         case 0:
  546.             if (sor1->minOffset < sor2->minOffset)
  547.                 if (sor2->minOffset <= sor1->maxOffset)
  548.                     {
  549.                     dest->minOffset = sor1->minOffset;
  550.                     dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  551.                     retVal = unionOne;
  552.                     }
  553.                 else retVal = firstIsLow;
  554.             else
  555.                 if (sor1->minOffset <= sor2->maxOffset)
  556.                     {
  557.                     dest->minOffset = sor2->minOffset;
  558.                     dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  559.                     retVal = unionOne;
  560.                     }
  561.                 else retVal = secondIsLow;
  562.             break;
  563.         
  564.         /* Case 1 is where the second SelectionOffsetRange is open on the right. */
  565.         
  566.         case 1:
  567.             if (sor2->minOffset > sor1->maxOffset) retVal = firstIsLow;
  568.             else
  569.                 {
  570.                 dest->minOffset = MyMin(sor1->minOffset, sor2->minOffset);
  571.                 dest->maxOffset = selectionExtremeEdge;
  572.                 retVal = unionOne;
  573.                 }
  574.             break;
  575.         
  576.         /* Case 2 is where the second SelectionOffsetRange is open on the left. */
  577.         
  578.         case 2:
  579.             if (sor2->maxOffset < sor1->minOffset) retVal = secondIsLow;
  580.             else
  581.                 {
  582.                 dest->minOffset = selectionExtremeEdge;
  583.                 dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  584.                 retVal = unionOne;
  585.                 }
  586.             break;
  587.         
  588.         /* Cases 3, 7, and 11 thru 15 are where one (or both) are full selections. */
  589.         
  590.         case 3:
  591.         case 7:
  592.         case 11:
  593.         case 12:
  594.         case 13:
  595.         case 14:
  596.         case 15:
  597.             dest->minOffset = dest->maxOffset = selectionExtremeEdge;
  598.             retVal = unionAll;
  599.             break;
  600.         
  601.         /* Case 4 is where the first SelectionOffsetRange is open on the right. */
  602.         
  603.         case 4:
  604.             if (sor1->minOffset > sor2->maxOffset) retVal = secondIsLow;
  605.             else
  606.                 {
  607.                 dest->minOffset = MyMin(sor1->minOffset, sor2->minOffset);
  608.                 dest->maxOffset = selectionExtremeEdge;
  609.                 retVal = unionOne;
  610.                 }
  611.             break;
  612.         
  613.         /* Case 5 is where both SelectionOffsetRanges are open on the right. */
  614.         
  615.         case 5:
  616.             dest->minOffset = MyMin(sor1->minOffset, sor2->minOffset);
  617.             dest->maxOffset = selectionExtremeEdge;
  618.             retVal = unionOne;
  619.             break;
  620.         
  621.         /* Case 6 is where the first SelectionOffsetRange is open on the right and the
  622.                 second SelectionOffsetRange is open on the right. */
  623.         
  624.         case 6:
  625.             if (sor1->minOffset > sor2->maxOffset) retVal = secondIsLow;
  626.             else
  627.                 {
  628.                 dest->minOffset = dest->maxOffset = selectionExtremeEdge;
  629.                 retVal = unionAll;
  630.                 }
  631.             break;
  632.         
  633.         /* Case 8 is where the first SelectionOffsetRange is open on the left. */
  634.         
  635.         case 8:
  636.             if (sor1->maxOffset < sor2->minOffset) retVal = firstIsLow;
  637.             else
  638.                 {
  639.                 dest->minOffset = selectionExtremeEdge;
  640.                 dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  641.                 retVal = unionOne;
  642.                 }
  643.             break;
  644.         
  645.         /* Case 9 is where the first SelectionOffsetRange is open on the left and the
  646.                 second SelectionOffsetRange is open on the left. */
  647.         
  648.         case 9:
  649.             if (sor2->minOffset > sor1->maxOffset) retVal = firstIsLow;
  650.             else
  651.                 {
  652.                 dest->minOffset = dest->maxOffset = selectionExtremeEdge;
  653.                 retVal = unionAll;
  654.                 }
  655.             break;
  656.         
  657.         /* Case 10 is where both SelectionOffsetRanges are open on the left. */
  658.         
  659.         case 10:
  660.             dest->minOffset = selectionExtremeEdge;
  661.             dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  662.             retVal = unionOne;
  663.             break;
  664.         
  665.         } /* end switch */
  666.     
  667.     return retVal;
  668.     } /* UnionOne */
  669.  
  670. /* ---------------------------------------------------------------------------------- */
  671.  
  672. void UnionSelection(SelectionHandle dest, SelectionHandle source)
  673.     {
  674.     Handle                                tempHandle;
  675.     long                                    destCount, sor1Count, sor2Count;
  676.     SelectionOffsetRange    *destSOR, *prevDestSOR, *sor1, *sor2, workSORSpace, workSORSpace2;
  677.     SelectionPtr                    destSIP, newSIP, sourceSIP;
  678.     short                                 maxAllocated = 10;
  679.     Size                                    newSize, sourceSize;
  680.     
  681.     /* Allocate the memory first, since we want it to be the last thing that moves mem. */
  682.     
  683.     newSize = siSize + maxAllocated * sizeof(SelectionOffsetRange);
  684.     tempHandle = NewHandle(newSize);
  685.     if (tempHandle == nil)
  686.         {GXPostGraphicsError(out_of_memory); return;}
  687.     destCount = 0;
  688.     newSIP = (SelectionPtr) *tempHandle;
  689.     
  690.     /* Validate inputs. */
  691.     
  692.     if (dest == nil || (destSIP = *dest) == nil)
  693.         {GXPostGraphicsError(parameter_is_nil); DisposeHandle(tempHandle); return;}
  694.     if (source == nil || (sourceSIP = *source) == nil)
  695.         {GXPostGraphicsError(parameter_is_nil); DisposeHandle(tempHandle); return;}
  696.     if (destSIP->type == simpleCaret || sourceSIP->type == simpleCaret)
  697.         {
  698. #ifdef debugging
  699.         GXPostGraphicsError(parameter_out_of_range);
  700. #endif
  701.         DisposeHandle(tempHandle);
  702.         return;
  703.         }
  704.     
  705.     /* If both the Selections are empty, just return. */
  706.     
  707.     if ((destSIP->type == emptySelection) && (sourceSIP->type == emptySelection))
  708.         {DisposeHandle(tempHandle); return;}
  709.     
  710.     /* Handle cases where one (but not both) selection is empty. If dest is empty, this
  711.             means just copy source to dest. If source is empty, just return. */
  712.     
  713.     if ((destSIP->type == emptySelection) && (sourceSIP->type != emptySelection))
  714.         { /* copy source to dest */
  715.         sourceSize = GetHandleSize((Handle) source);
  716.         SetHandleSize((Handle) dest, sourceSize);
  717.         if (MemError() != noErr) GXPostGraphicsError(out_of_memory);
  718.         else BlockMove(*((Handle) source), *((Handle) dest), sourceSize); /* mem might have moved! */
  719.         DisposeHandle(tempHandle);
  720.         return;
  721.         } /* end copy source to dest */
  722.     else if ((destSIP->type != emptySelection) && (sourceSIP->type == emptySelection))
  723.         {DisposeHandle(tempHandle); return;}
  724.     
  725.     /* At this gxPoint both selections are range selections. We can therefore begin to walk
  726.             down the two selections in parallel, creating the new selection from the combo.
  727.             We "seed" the process by creating the first new entry before entering the loop;
  728.             this lets us get some of the testing code out of the loop proper. */
  729.     
  730.     prevDestSOR = destSOR = &newSIP->data.range.ranges[0];
  731.     sor1 = &destSIP->data.range.ranges[0];
  732.     sor2 = &sourceSIP->data.range.ranges[0];
  733.     sor1Count = destSIP->data.range.rangeCount;
  734.     sor2Count = sourceSIP->data.range.rangeCount;
  735.     destCount = 1;
  736.     
  737.     HLock((Handle) dest);
  738.     HLock((Handle) source);
  739.     
  740.     switch (UnionOne(sor1, sor2, &workSORSpace))
  741.         {
  742.         case unionAll:
  743.             *destSOR = workSORSpace;
  744.             sor1Count = sor2Count = 0;    /* forces following loop to be skipped */
  745.             break;
  746.         case unionOne:
  747.             *destSOR++ = workSORSpace;
  748.             sor1Count--; sor1++;
  749.             sor2Count--; sor2++;
  750.             break;
  751.         case firstIsLow:
  752.             *destSOR++ = *sor1++;
  753.             sor1Count--;
  754.             break;
  755.         case secondIsLow:
  756.             *destSOR++ = *sor2++;
  757.             sor2Count--;
  758.             break;
  759.         } /* end switch */
  760.     
  761.     while ((sor1Count > 0) || (sor2Count > 0))
  762.         { /* main merge loop */
  763.         if ((sor1Count > 0) && (sor2Count > 0))
  764.             { /* still have both */
  765.             switch (UnionOne(sor1, sor2, &workSORSpace))
  766.                 {
  767.                 case unionAll:
  768.                     newSIP->data.range.ranges[0] = workSORSpace;
  769.                     destCount = 1;
  770.                     sor1Count = sor2Count = 0;
  771.                     break;
  772.                 case unionOne:
  773.                     switch (UnionOne(prevDestSOR, &workSORSpace, &workSORSpace2))
  774.                         {
  775.                         case unionAll:
  776.                             newSIP->data.range.ranges[0] = workSORSpace2;
  777.                             destCount = 1;
  778.                             sor1Count = sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  779.                             break;
  780.                         case unionOne:
  781.                             *prevDestSOR = workSORSpace2;
  782.                             break;
  783.                         case firstIsLow:
  784.                             if (destCount == maxAllocated)
  785.                                 GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  786.                             *destSOR = workSORSpace;
  787.                             destCount++;
  788.                             prevDestSOR = destSOR++;
  789.                             break;
  790.                         case secondIsLow:
  791.                             GXPostGraphicsError(internal_layout_error);
  792.                             sor1Count = sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  793.                             break;
  794.                         } /* end switch */
  795.                     sor1++; sor2++;
  796.                     sor1Count--; sor2Count--;
  797.                     break;
  798.                 case firstIsLow:
  799.                     switch (UnionOne(prevDestSOR, sor1, &workSORSpace))
  800.                         {
  801.                         case unionAll:
  802.                             newSIP->data.range.ranges[0] = workSORSpace;
  803.                             destCount = 1;
  804.                             sor1Count = 1;    /* dec'd to 0 below, forces loop exit */
  805.                             sor2Count = 0;
  806.                             break;
  807.                         case unionOne:
  808.                             *prevDestSOR = workSORSpace;
  809.                             break;
  810.                         case firstIsLow:
  811.                             if (destCount == maxAllocated)
  812.                                 GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  813.                             *destSOR = *sor1;
  814.                             destCount++;
  815.                             prevDestSOR = destSOR++;
  816.                             break;
  817.                         case secondIsLow:
  818.                             GXPostGraphicsError(internal_layout_error);
  819.                             sor1Count = 1;    /* dec'd to 0 below, forces loop exit */
  820.                             break;
  821.                         } /* end switch */
  822.                     sor1++;
  823.                     sor1Count--;
  824.                     break;
  825.                 case secondIsLow:
  826.                     switch (UnionOne(prevDestSOR, sor2, &workSORSpace))
  827.                         {
  828.                         case unionAll:
  829.                             newSIP->data.range.ranges[0] = workSORSpace;
  830.                             destCount = 1;
  831.                             sor1Count = 0;
  832.                             sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  833.                             break;
  834.                         case unionOne:
  835.                             *prevDestSOR = workSORSpace;
  836.                             break;
  837.                         case firstIsLow:
  838.                             if (destCount == maxAllocated)
  839.                                 GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  840.                             *destSOR = *sor2;
  841.                             destCount++;
  842.                             prevDestSOR = destSOR++;
  843.                             break;
  844.                         case secondIsLow:
  845.                             GXPostGraphicsError(internal_layout_error);
  846.                             sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  847.                             break;
  848.                         } /* end switch */
  849.                     sor2++;
  850.                     sor2Count--;
  851.                     break;
  852.                 } /* end switch */
  853.             } /* end still have both */
  854.         else if (sor1Count > 0)
  855.             { /* still have first */
  856.             switch (UnionOne(prevDestSOR, sor1, &workSORSpace))
  857.                 {
  858.                 case unionAll:
  859.                     newSIP->data.range.ranges[0] = workSORSpace;
  860.                     destCount = 1;
  861.                     sor1Count = 1;    /* dec'd to 0 below, forces loop exit */
  862.                     sor2Count = 0;
  863.                     break;
  864.                 case unionOne:
  865.                     *prevDestSOR = workSORSpace;
  866.                     break;
  867.                 case firstIsLow:
  868.                     if (destCount == maxAllocated)
  869.                         GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  870.                     *destSOR = *sor1;
  871.                     destCount++;
  872.                     prevDestSOR = destSOR++;
  873.                     break;
  874.                 case secondIsLow:
  875.                     GXPostGraphicsError(internal_layout_error);
  876.                     sor1Count = 1;    /* dec'd to 0 below, forces loop exit */
  877.                     break;
  878.                 } /* end switch */
  879.             sor1++;
  880.             sor1Count--;
  881.             } /* end still have first */
  882.         else
  883.             { /* still have second */
  884.             switch (UnionOne(prevDestSOR, sor2, &workSORSpace))
  885.                 {
  886.                 case unionAll:
  887.                     newSIP->data.range.ranges[0] = workSORSpace;
  888.                     destCount = 1;
  889.                     sor1Count = 0;
  890.                     sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  891.                     break;
  892.                 case unionOne:
  893.                     *prevDestSOR = workSORSpace;
  894.                     break;
  895.                 case firstIsLow:
  896.                     if (destCount == maxAllocated)
  897.                         GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  898.                     *destSOR = *sor2;
  899.                     destCount++;
  900.                     prevDestSOR = destSOR++;
  901.                     break;
  902.                 case secondIsLow:
  903.                     GXPostGraphicsError(internal_layout_error);
  904.                     sor2Count = 1;    /* dec'd to 0 below, forces loop exit */
  905.                     break;
  906.                 } /* end switch */
  907.             sor2++;
  908.             sor2Count--;
  909.             } /* end still have second */
  910.         } /* end main merge loop */
  911.     
  912.     HUnlock((Handle) dest);
  913.     HUnlock((Handle) source);
  914.     
  915.     newSize = siSize + destCount * sizeof(SelectionOffsetRange);
  916.     SetHandleSize((Handle) dest, newSize);
  917.     if (MemError() != noErr) GXPostGraphicsError(out_of_memory);
  918.     destSIP = *dest;
  919.     
  920.     destSIP->type = (SelectionType) ((destCount == 1) ? simpleRange : discontiguousRange);
  921.     destSIP->data.range.rangeCount = destCount;
  922.     sor1 = &newSIP->data.range.ranges[0];
  923.     destSOR = &destSIP->data.range.ranges[0];
  924.     while (destCount-- > 0) *destSOR++ = *sor1++;
  925.     
  926.     DisposeHandle(tempHandle);
  927.     
  928.     } /* UnionSelection */
  929.  
  930. /* ---------------------------------------------------------------------------------- */
  931.  
  932. void SectSelection(SelectionHandle dest, SelectionHandle source)
  933.     {
  934.     SelectionHandle sourceCopy = source;
  935.     if (HandToHand((Handle *) &sourceCopy) != noErr)
  936.         {GXPostGraphicsError(out_of_memory); return;}
  937.     InvertSelection(dest);
  938.     InvertSelection(sourceCopy);
  939.     UnionSelection(dest, sourceCopy);
  940.     DisposeHandle((Handle) sourceCopy);
  941.     InvertSelection(dest);
  942.     } /* SectSelection */
  943.  
  944. /* ---------------------------------------------------------------------------------- */
  945.  
  946. void XorSelection(SelectionHandle dest, SelectionHandle source)
  947.     {
  948.     SelectionHandle sourceCopy = source;
  949.     if (HandToHand((Handle *) &sourceCopy) != noErr)
  950.         {GXPostGraphicsError(out_of_memory); return;}
  951.     SectSelection(sourceCopy, dest);
  952.     UnionSelection(dest, source);
  953.     DiffSelection(dest, sourceCopy);
  954.     DisposeHandle((Handle) sourceCopy);
  955.     } /* XorSelection */
  956.  
  957. /* ---------------------------------------------------------------------------------- */
  958.  
  959. void DiffSelection(SelectionHandle dest, SelectionHandle source)
  960.     {
  961.     SelectionHandle sourceCopy = source;
  962.     if (HandToHand((Handle *) &sourceCopy) != noErr)
  963.         {GXPostGraphicsError(out_of_memory); return;}
  964.     InvertSelection(sourceCopy);
  965.     SectSelection(dest, sourceCopy);
  966.     DisposeHandle((Handle) sourceCopy);
  967.     } /* DiffSelection */
  968.  
  969. /* ---------------------------------------------------------------------------------- */
  970.  
  971. gxShape GetLayoutSelection(
  972.     gxShape                     layout,
  973.     SelectionHandle selection,
  974.     SelectionOffset lineStart,
  975.     gxHighlightType     highlightType,
  976.     gxCaretType             caretType)
  977.  
  978.     {
  979.     long                                    maxOffset;
  980.     SelectionOffset             off1, off2;
  981.     SelectionOffsetRange    *sor;
  982.     SelectionPtr                    destSIP;
  983.     gxShape                                 retShape, workShape;
  984.     short                                 count;
  985.     
  986.     if (layout == nil || selection == nil || (destSIP = *selection) == nil)
  987.         {GXPostGraphicsError(parameter_is_nil); return nil;}
  988.     
  989.     HLock((Handle) selection);
  990.     maxOffset = GXGetLayout(layout, nil, nil, nil, nil, nil, nil, nil, nil, nil);
  991.     
  992.     switch (destSIP->type)
  993.         {
  994.         case emptySelection:
  995.             retShape = GXNewShape(gxEmptyType);
  996.             break;
  997.         case simpleCaret:
  998.             off1 = destSIP->data.caret.offset - lineStart;
  999.             if (off1 < 0) off1 = 0;
  1000.             else if (off1 > maxOffset) off1 = maxOffset;
  1001.             retShape = GXGetLayoutCaret(
  1002.                 layout,
  1003.                 (gxByteOffset) off1,
  1004.                 highlightType,
  1005.                 caretType,
  1006.                 nil);
  1007.             break;
  1008.         case simpleRange:
  1009.             if (destSIP->data.range.ranges[0].minOffset == selectionExtremeEdge) off1 = 0;
  1010.             else
  1011.                 {
  1012.                 off1 = destSIP->data.range.ranges[0].minOffset - lineStart;
  1013.                 if (off1 < 0) off1 = 0;
  1014.                 else if (off1 > maxOffset) off1 = maxOffset;
  1015.                 }
  1016.             if (destSIP->data.range.ranges[0].maxOffset == selectionExtremeEdge) off2 = maxOffset;
  1017.             else
  1018.                 {
  1019.                 off2 = destSIP->data.range.ranges[0].maxOffset - lineStart;
  1020.                 if (off2 < 0) off2 = 0;
  1021.                 else if (off2 > maxOffset) off2 = maxOffset;
  1022.                 }
  1023.             retShape = GXGetLayoutHighlight(
  1024.                 layout,
  1025.                 (gxByteOffset) off1,
  1026.                 (gxByteOffset) off2,
  1027.                 highlightType,
  1028.                 nil);
  1029.             break;
  1030.         case discontiguousRange:
  1031.             /* In this case, we need to build a multi-gxPolygon gxPolygons gxShape. */
  1032.             count = (short) destSIP->data.range.rangeCount;
  1033.             sor = &destSIP->data.range.ranges[0];
  1034.             retShape = GXNewShape(gxEmptyType);
  1035.             workShape = GXNewShape(gxEmptyType);
  1036.             while (count--)
  1037.                 { /* gather loop */
  1038.                 if (sor->minOffset == selectionExtremeEdge) off1 = 0;
  1039.                 else
  1040.                     {
  1041.                     off1 = sor->minOffset - lineStart;
  1042.                     if (off1 < 0) off1 = 0;
  1043.                     else if (off1 > maxOffset) off1 = maxOffset;
  1044.                     }
  1045.                 if (sor->maxOffset == selectionExtremeEdge) off2 = maxOffset;
  1046.                 else
  1047.                     {
  1048.                     off2 = sor->maxOffset - lineStart;
  1049.                     if (off2 < 0) off2 = 0;
  1050.                     else if (off2 > maxOffset) off2 = maxOffset;
  1051.                     }
  1052.                 GXGetLayoutHighlight(
  1053.                     layout,
  1054.                     (gxByteOffset) off1,
  1055.                     (gxByteOffset) off2,
  1056.                     highlightType,
  1057.                     workShape);
  1058.                 GXUnionShape(retShape, workShape);
  1059.                 sor++;
  1060.                 } /* end gather loop */
  1061.             GXDisposeShape(workShape);
  1062.             break;
  1063.         } /* end switch */
  1064.     
  1065.     HUnlock((Handle) selection);
  1066.     return retShape;
  1067.     } /* GetLayoutSelection */
  1068.  
  1069. /* NewDiscontiguousSelection creates a discontiguous selection. It returns an empty
  1070.         selection if the argument is nil. */
  1071.  
  1072. SelectionHandle NewDiscontiguousSelection(SelectionRanges *ranges)
  1073.     {
  1074.     long                                    i;
  1075.     SelectionHandle             returnedSelection, workingSelection;
  1076.     SelectionOffsetRange    *thisSOR;
  1077.     
  1078.     returnedSelection = NewEmptySelection();
  1079.     
  1080.     if (ranges)
  1081.         {
  1082.         thisSOR = ranges->ranges;
  1083.         workingSelection = NewRangeSelection(thisSOR++);
  1084.         
  1085.         for (i = ranges->rangeCount - 1; i >= 0; --i)
  1086.             {
  1087.             UnionSelection(returnedSelection, workingSelection);
  1088.             SetRangeSelection(workingSelection, thisSOR++);
  1089.             }
  1090.         
  1091.         DisposeSelection(workingSelection);
  1092.         }
  1093.     
  1094.     return returnedSelection;
  1095.     } /* NewDiscontiguousSelection */
  1096.